/**
 * 
 */
package juf.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;

/** This is the list of generic object instances, can show instances of any generic object type.
 * Double-Clicking on an instance will make it appear for editing/viewing in a JufForm.
 * <p/>
 * This should be enclosed in a JScrollPane to make it scrolling.
 * <p/>
 * The list contains JufListPanels to represent the instances, which should not be accessed directly. These are
 * bound to the objects, and the objects are transparently accessed through them.
 * <p/>
 * The object's tostring defines the name of the object by default, but this can be overridden using annotations
 * <p/>
 * This url is good for Jlist stuff http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html
 * 
 * @author Dale Halliwell
 *
 */
@SuppressWarnings("serial")
public class JufInstanceList<T> extends JList {

	private Vector<JufListPanel> jufPanelVector = new Vector<JufListPanel>();  //to store the list items
	public enum SortOrders {ASCENDING, DESCENDING, NATURAL}; //valid orders by which to sort items in the list
	
	/**Create a new JufInstanceList
	 * @param ts collectiun of instances of type T
	 */
	public JufInstanceList(java.util.Collection<T> ts){
		this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		this.setLayoutOrientation(JList.VERTICAL);
		
		
		//cell renderer via anonymous classes, cool :)
		this.setCellRenderer( new ListCellRenderer() {
			    public Component getListCellRendererComponent
			     (JList list, Object value, int index, 
			      boolean isSelected,boolean cellHasFocus) {
				      JufListPanel component = (JufListPanel)value; 
				      if (!component.isEnabled()){ //if disabled, gray out
				    	  component.setBackground (Color.getHSBColor((float)0.0, (float)0.0, (float)0.85));
				      }
				      else { //display it differently based on selection state
				    	  component.setSelected(isSelected);
				      }
				      return (Component) component;
			      }
		}); //needed to render a JufListPanel as a list element
		
		//add all the instances
		Iterator<T> t_it = ts.iterator();
		while(t_it.hasNext())
			this.add(t_it.next());		

		this.refresh(); //update the form
	}
	
	
	/**
	 * Add an object instance to the list. Does not display it right away, will need to call refresh().
	 * This is so many items can be added to the list, then they can be displayed at once.
	 * 
	 * @param t object to add
	 */
	public void add(T t){
		
		//set up the panel that will contain these object
	    JufListPanel panel = new JufListPanel(t);
	    panel.setForeground(Color.gray); //set default coloring
		panel.setBackground(Color.white);	
		panel.setPreferredSize(new Dimension (this.getWidth(), 30)); //sizing
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); //layout
		panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); //margin
		panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(new Color(234,234,234)),panel.getBorder())); //border

		//add to the vector of JufPanels
		jufPanelVector.add(panel); //but the list is not updated yet
	}
	
	/**
	 * Use this to refresh the display of the list with sorting. 
	 * Sorts the list according to the toString() representation of the object (for now).
	 */
	public void refresh(SortOrders s){	
		//only need to sort if either of these arguments has been given
		if (s.equals(SortOrders.ASCENDING)) 
			Collections.sort(jufPanelVector);
		else if (s.equals(SortOrders.DESCENDING))
			Collections.reverse(jufPanelVector);	
		//otherwise it will just be natural ordering
		this.setListData(jufPanelVector); 
		this.validate();		
	}
	
	/**
	 * Refreshes the display of the list
	 */
	public void refresh(){
		refresh(SortOrders.NATURAL);
	}
	
	/**
	 * Redraw the list from the vector
	 */	
	public void update(){
//		//re-add all the instances
//		this.add(arg0)
//		Iterator<T> t_it = ts.iterator();
//		while(t_it.hasNext())
//			this.add(t_it.next());		
//
//		this.refresh(); //update the form		
	}
	
	/** 
	 * Remove an element from the list.
	 */
	public void remove(int index){
		jufPanelVector.removeElementAt(index);
		this.refresh();
	}
	

	
	/** Used by the JufForm to get the object bound to the currently selected list item.
	 * Returns null if no object is selected.
	 * 
	 * @return the object bound to the currently selected list item, or null if no object is selected.
	 */
	public T getSelectedObject(){
		return ((JufListPanel) this.getSelectedValue()).getObject();
	}
	
	
	public T getCopy(){
		JufListPanel panel = (JufListPanel) this.getSelectedValue();
		JufListPanel panel2 = (JufListPanel) panel.clone();
		if (panel2 != null){
			T o = panel2.getObject();
			return o;
		}
		return null;
		
	}

	
	/** JufListPanel<br/>
	 * Inner class that binds an arbitrary object to a JPanel that can be displayed in the list.
	 * This way we only need to store a reference to JufListPanels, not maintain separate lists
	 * of JPanels and Objects, which seems awkward.
	 * 
	 * @author Dale Halliwell
	 * 
	 * nb. the JufListPanel's comparator behavior should be the same as the object's
	 */
	private class JufListPanel extends JPanel implements Comparable<JufListPanel>, Cloneable{
		private T object; //the object represented by this JPanel in the list

		private JLabel arrowLabel; //the "selection arrow"
		private ImageIcon whiteArrow; //the arrow icons to use
		private ImageIcon normalArrow;	
		
		//create a new JufPanel bound to an object
		public JufListPanel(T t) {
			
			this.setObject(t);//set the object
			
			whiteArrow = new ImageIcon("images/arrow_white.gif"); //load the two arrow icons
			normalArrow = new ImageIcon("images/arrow.gif");	
			
			//the little "selection arrow" icon and a bit of whitespace
			arrowLabel = new JLabel("   ", new ImageIcon("images/arrow.gif"),JLabel.LEFT);
			this.add(arrowLabel);
			
			//the label for the name, with the folder icon
			JLabel instanceLabel = new JLabel(t.toString(), new ImageIcon("images/foldericon_sml.gif"),JLabel.LEFT); 
			instanceLabel.setIconTextGap(20); //space between text and icon 
			this.add(instanceLabel);
		}
		
		
		/** Used by the JufCellRenderer to display the cell in either a "selected" or "not selected" state.
		 * 
		 * @param isSelected whether or not the cell is selected
		 */
		public void setSelected(boolean isSelected){
			if (isSelected) {
				arrowLabel.setIcon(whiteArrow);
				this.setBackground(new Color(91,126,192)); //blue
				//this.setForeground(Color.white);				
			}
			else {
				arrowLabel.setIcon(normalArrow);	
				this.setBackground(Color.white);
				this.setForeground(Color.black);
			}				
		}

		//generic accessors for the object bound to this JufPanel
		public void setObject(T t) {
			this.object = t;
		}

		public T getObject() {
			return object;
		}

		
		 @SuppressWarnings("unchecked")
		public Object clone(){
		        try{
//		        	System.out.println("juflistpanel cloned");
		        	
		        	JufListPanel aobj = (JufListPanel)super.clone();
		        	
		        	Class cl=Class.forName(aobj.object.getClass().getName());
		        	Method mthd=cl.getMethod("clone");
		            
		        	Object output = mthd.invoke(aobj.object);
		            aobj.object = (T) output;


		            return aobj;
		        }catch( CloneNotSupportedException e ){
		        	System.out.println("This issue has occurred because you have not implemented 'cloneable' interface in your class.");
		            return null;
		        } catch (ClassNotFoundException e) {
		        	System.out.println("This issue has occurred because we were unable to find the class to clone.");
//					e.printStackTrace();
				} catch (SecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					System.out.println("This issue has occurred because you have not created a clone method in your class.");
//					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					System.out.println("This issue has occurred because of an IllegalArgumentException encountered during the cloning of an instance.");
//					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return null;
		 }
		

		/** 
		 * Required in order to implement the Comparable interface, for sorting and equality tests
		 */
		@Override
		public int compareTo(JufListPanel o) {
			return this.getObject().toString().compareTo(o.getObject().toString()); //simple comparison based on string

		}
		


	} //end JufPanel
	

} //end JufInstanceList
